﻿using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace XFControls.Utils
{
    public static class XFBitmapFactory
    {
        public static Bitmap GetBitmap(string file)
        {
            return GetBitmap(file, false);
        }

        public static Bitmap GetBitmap(string file, bool ex)
        {
            try
            {
                return new Bitmap(file);
            }
            catch (OutOfMemoryException)
            {
                if (ex) throw;

                XFControlUtils.LogInfo(XFControlUtils.GetMemoryStatus(), "OOME");

                GC.Collect();
                GC.WaitForPendingFinalizers();
                return GetBitmap(file, true);
            }

        }

        public static Bitmap GetBitmap(Image image)
        {
            return new Bitmap(image);
        }

        public static Bitmap GetBitmap(Stream stream)
        {
            return GetBitmap(stream, false);
        }
        public static Bitmap GetBitmap(Stream stream, bool ex)
        {
            try
            {
                return new Bitmap(stream);
            }
            catch (OutOfMemoryException)
            {
                if (ex) throw;

                XFControlUtils.LogInfo(XFControlUtils.GetMemoryStatus(), "OOME");

                GC.Collect();
                GC.WaitForPendingFinalizers();
                return GetBitmap(stream, true);
            }

        }

        public static Bitmap GetBitmap(int width, int height, PixelFormat format)
        {
            return new Bitmap(width, height, format);
        }

        public static Bitmap GetBitmap(int width, int height)
        {
            return CreateBitmap(width, height, false);
        }

        private static Bitmap CreateBitmap(int width, int height, bool ex)
        {
            try
            {
                //return new Bitmap(width, height, PixelFormat.Format16bppRgb565);
                return new Bitmap(width, height);
            }
            catch (OutOfMemoryException)
            {
                if (ex) throw;

                XFControlUtils.LogInfo(XFControlUtils.GetMemoryStatus(), "OOME");

                GC.Collect();
                GC.WaitForPendingFinalizers();
                return CreateBitmap(width, height, true);
            }
        }

        public static Bitmap IBitmapImageToBitmap(IBitmapImage imageBitmap)
        {
            Size size;
            if (imageBitmap == null)
            {
                throw new ArgumentNullException();
            }
            imageBitmap.GetSize(out size);

            var rect = new Rectangle(0, 0, size.Width, size.Height);
            BitmapData lockedBitmapData;

            imageBitmap.LockBits(ref rect, 0, PixelFormatID.PixelFormat16bppRGB565, out lockedBitmapData);
            Bitmap bitmap = new Bitmap((int)lockedBitmapData.Width, (int)lockedBitmapData.Height, PixelFormat.Format16bppRgb565);
            System.Drawing.Imaging.BitmapData bitmapdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, PixelFormat.Format16bppRgb565);

            CopyMemory(bitmapdata.Scan0, lockedBitmapData.Scan0, (int)lockedBitmapData.Height * lockedBitmapData.Stride);
            imageBitmap.UnlockBits(ref lockedBitmapData);
            bitmap.UnlockBits(bitmapdata);

            return bitmap;
        }

        private static void CopyMemory(IntPtr pDst, IntPtr pSrc, int cbSize)
        {
            if (((pDst == IntPtr.Zero) || (pSrc == IntPtr.Zero)) || (((cbSize <= 0) || (pDst.ToInt32() == -1)) || (pSrc.ToInt32() == -1)))
            {
                throw new ArgumentException();
            }
            byte[] destination = new byte[0x10000];
            while (cbSize > 0x10000)
            {
                Marshal.Copy(pSrc, destination, 0, 0x10000);
                Marshal.Copy(destination, 0, pDst, 0x10000);
                cbSize -= 0x10000;
                pDst = (IntPtr)(((int)pDst) + 0x10000);
                pSrc = (IntPtr)(((int)pSrc) + 0x10000);
            }
            Marshal.Copy(pSrc, destination, 0, cbSize);
            Marshal.Copy(destination, 0, pDst, cbSize);
        }

    }

    public class XFBitmap : IDisposable
    {
        [DllImport("coredll.dll", EntryPoint = "CreateCompatibleDC")]
        static extern IntPtr CreateCompatibleDC(IntPtr hdc);

        [DllImport("coredll.dll", EntryPoint = "CreateDIBSection", SetLastError = true)]
        static extern IntPtr CreateDIBSection(IntPtr hdc, [In] ref BITMAPINFO pbmi, uint pila, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);

        [StructLayout(LayoutKind.Sequential)]
        struct BITMAPINFO
        {
            public Int32 biSize;
            public Int32 biWidth;
            public Int32 biHeight;
            public Int16 biPlanes;
            public Int16 biBitCount;
            public Int32 biCompression;
            public Int32 biSizeImage;
            public Int32 biXPelsPerMeter;
            public Int32 biYPelsPerMeter;
            public Int32 biClrUsed;
            public Int32 biClrImportant;
        }

        private BITMAPINFO bih;
        private IntPtr pBits;
        private IntPtr hBitmap;
        private IntPtr hbmOld;
        private IntPtr hDC;

        private int _height;
        private int _width;

        public XFBitmap(int width, int height)
        {
            _width = width;
            _height = height;

            CreateUnmanagedResources();
        }

        public Graphics GetGraphics()
        {
            return Graphics.FromImage(GetBitmap());
        }

        public Bitmap GetBitmap()
        {
            //return Bitmap.FromHbitmap(hBitmap);
            Bitmap ret = null;

            using (MemoryStream ms = new MemoryStream())
            {
                byte[] imageData = new byte[GetImageSize()];
                using (BinaryWriter w = new BinaryWriter(ms))
                {
                    // copy image data into imageData buffer 
                    Marshal.Copy(pBits, imageData, 0, imageData.Length);

                    // write imageData to stream 
                    w.Write(imageData);
                    w.Close();
                }

                using (MemoryStream bms = new MemoryStream(imageData))
                    ret = new Bitmap(bms);
                ms.Close();
            }

            return ret;
        }

        private void CreateUnmanagedResources()
        {
            // for safety, clean up anything that was already allocated 
            ReleaseUnmanagedResources();

            bih = new BITMAPINFO
                  {
                      biBitCount = 24,
                      biClrImportant = 0,
                      biClrUsed = 0,
                      biCompression = 0,
                      biHeight = _height,
                      biPlanes = 1,
                      biSize = (Marshal.SizeOf(typeof(BITMAPINFO))),
                      biSizeImage = 0,
                      biWidth = _width,
                      biXPelsPerMeter = 0,
                      biYPelsPerMeter = 0
                  };

            hDC = CreateCompatibleDC(IntPtr.Zero);
            pBits = IntPtr.Zero;
            hBitmap = CreateDIBSection(hDC, ref bih, 0, out pBits, IntPtr.Zero, 0);
            //hBitmap = CreateDIBSection(IntPtr.Zero, ref bih, 0, out pBits, IntPtr.Zero, 0);

            if (hBitmap == IntPtr.Zero)
            {
                var error = Marshal.GetLastWin32Error();

            }
            hbmOld = XFGraphicExtensions.SelectObject(hDC, hBitmap);


            //IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
            //IntPtr hbm0 = CreateDIBSection(IntPtr.Zero, ref bih, 0, out bits0, IntPtr.Zero, 0);
            //Bitmap b0 = Bitmap.FromHbitmap(hbm0);

        }

        private void ReleaseUnmanagedResources()
        {
            if (hbmOld != IntPtr.Zero)
                XFGraphicExtensions.SelectObject(hDC, hbmOld);

            if (hBitmap != IntPtr.Zero)
                XFGraphicExtensions.DeleteObject(hBitmap);

            if (hDC != IntPtr.Zero)
                XFGraphicExtensions.DeleteDC(hDC);
        }

        private int GetImageSize()
        {
            return (CalcRowSize(bih.biWidth * bih.biBitCount) * bih.biHeight);
        }

        private static int CalcRowSize(int bits)
        {
            return ((((bits) + 31) / 32) * 4);
        }

        #region IDisposable Members

        public void Dispose()
        {
            ReleaseUnmanagedResources();
        }

        ~XFBitmap()
        {
            ReleaseUnmanagedResources();
        }

        #endregion
    }
}
